package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.NewsAutoScanConstants;
import com.heima.common.constants.message.WmNewsMessageConstants;
import com.heima.common.constants.wemadia.WemediaConstants;
import com.heima.common.exception.CustException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.threablocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dtos.NewsAuthDto;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.model.wemedia.vos.WmNewsVo;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Value("${file.oss.web-site}")
    String webSite;

    @Autowired
    KafkaTemplate<String, String> kafkaTemplate;
    @Autowired
    WmNewsMapper wmNewsMapper;

    //查询文章的业务方法
    @Override
    public ResponseResult findList(WmNewsPageReqDto dto) {
        //1.校验参数  dto 分页 判断登录状态
        if (dto == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        dto.checkParam();
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }
        //2.封装查询条件
            //2.1. 分页条件
        IPage page = new Page(dto.getPage(), dto.getSize());
            //2.2查询条件
        LambdaQueryWrapper<WmNews> wrapper = Wrappers.<WmNews>lambdaQuery();
                //2.2.1.判断标题条件
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            wrapper.like(WmNews::getTitle, dto.getKeyword());
        }
                //2.2.2.频道
        if (dto.getChannelId() != null) {
            wrapper.eq(WmNews::getChannelId, dto.getChannelId());
        }
                //2.2.3.状态
        if (dto.getStatus() != null) {
            wrapper.eq(WmNews::getStatus, dto.getStatus());
        }
                //2.2.4.开始，结束时间
        if (dto.getBeginPubDate() != null && dto.getEndPubDate() != null) {
            wrapper.between(WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());
        }
                //2.2.5.当前登录用户id
        wrapper.eq(WmNews::getUserId, user.getId());
                //2.2.6.按照创建时间降序排序
        wrapper.orderByDesc(WmNews::getCreatedTime);
        //3.执行查询,返回结果加上图片访问前缀
        IPage<WmNews> iPage = page(page, wrapper);
        PageResponseResult result = new PageResponseResult(dto.getPage(), dto.getSize(), iPage.getTotal(), iPage.getRecords());
        result.setHost(webSite);
        return null;
    }

    //发表文章的业务方法
    @Override
    public ResponseResult submitNews(WmNewsDto wmNewsDto) {
        //1.检验参数
        if (wmNewsDto == null || StringUtils.isBlank(wmNewsDto.getContent())) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章内容不能为空");
        }
        Short status = wmNewsDto.getStatus();
        if (status == null || (status.intValue() != 0 && status.intValue() != 1)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ERROR,"文章状态错误");
        }
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }
        //将dto参数里面的值设置到wmNews中
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(wmNewsDto,wmNews);
        //封装图片
        List<String> imagesList = wmNewsDto.getImages();
        if (imagesList == null && imagesList.size() > 0) {
            wmNews.setImages(imageToStr(imagesList));
        }
        //如果文章布局是自动，需要设置为null
        if (wmNewsDto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            wmNewsDto.setType(null);
        }
        //用户
        wmNews.setUserId(user.getId());
        //2.保存或修改文章
        saveOrUpdateWmNews(wmNews);
        //如果是草稿，不用处理关联关系
        if (wmNewsDto.getStatus().equals(WemediaConstants.WM_NEWS_DRAFT_STATUS)) {
            return ResponseResult.okResult();
        }
        //3.保存文章和素材的关联关系
        //3.1.先抽取文章内容中所涉及的所有图片url
        List<Map> contentMaps = JSONArray.parseArray(wmNewsDto.getContent(), Map.class);
        List<String> contentUrlList = parseContentImages(contentMaps);
        //3.2.保存文章内容和素材的关联关系
        if (contentUrlList != null && contentUrlList.size() > 0) {
            //保存内容中图片和素材的关联关系
            saveRelativeInfo(wmNews.getId(),contentUrlList,WemediaConstants.WM_CONTENT_REFERENCE);
        }

        //3.3.保存文章封面和素材的关联关系
        saveRelativeInfoForCover(wmNews,contentUrlList,wmNewsDto);
        //发送自动审核文章的消息通知
        Map map = new HashMap();
        map.put("wmNewsId", wmNews.getId());
        kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC, JSON.toJSONString(map));
        log.info("自动审核的消息通知成功发送  主题：{}  文章id：{}", NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC, wmNews.getId());

        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult findWmNewsById(Integer id) {
        //检查id
        if (id == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"id不能为空");
        }
        //查询文章
        WmNews wmNews = getById(id);
        if (wmNews == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"该文章不存在");
        }
        //返回结果   添加访问前缀
        ResponseResult responseResult = ResponseResult.okResult(wmNews);
        responseResult.setHost(webSite);
        return responseResult;
    }

    @Override
    public ResponseResult delNews(Integer id) {
        //校验id
        if (id == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"id不能为空");
        }
        //根据id查询自媒体文章
        WmNews wmNews = getById(id);
        if (wmNews == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"该文章信息不存在");
        }
        //判断文章是否已发布并且上架
        if (wmNews.getStatus().equals(WemediaConstants.WM_NEWS_PUBLISH_STATUS) && wmNews.getEnable().equals(WemediaConstants.WM_NEWS_UP)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ERROR,"该文章已发布并上架，不可删除");

        }
        //删除文章和素材的关联关系
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getId, id));
        //删除文章信息
        removeById(id);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {
        //1.校验参数
        if (dto == null || dto.getId() == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "id不能为空");
        }
        Short enable = dto.getEnable();
        if (enable == null || (enable.intValue() != 0 && enable.intValue() != 1)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ERROR,"文章状态异常");
        }
        //2.根据id查询文章
        WmNews wmNews = getById(dto.getId());
        if (wmNews == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章信息错误");
        }
        //3.判断文章是否发布  状态 9
        if (wmNews.getStatus().intValue() != 9) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ERROR, "该文章未发布,无法上架");
        }
        //4.修改文章上架字段
        update(Wrappers.<WmNews>lambdaUpdate()
                .set(WmNews::getEnable, enable)
                .eq(WmNews::getId, dto.getId()));
        //5.返回结果
        //TODO 同步app端文章上下架
        if (wmNews.getArticleId() != null) {
            Map map = new HashMap();
            map.put("articleId", wmNews.getArticleId());
            map.put("enable", enable);
            kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JSON.toJSONString(map));
            log.info("发送文章上下架消息 到主题： {}，消息的内容：{}", WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, map);

        }

        //TODO 同步es文章索引库

        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult<List<Integer>> findRelease() {
        //1.查询状态为4或8 并且 发布时间小于等于当前时间
        List<WmNews> list = list(Wrappers.<WmNews>lambdaQuery()
                .in(WmNews::getStatus, 8, 4)
                .le(WmNews::getPublishTime, new Date())
                .select(WmNews::getId)
        );
        List<Integer> newsId = list.stream().map(WmNews::getId).collect(Collectors.toList());
        return ResponseResult.okResult(newsId);
    }

    @Override
    public ResponseResult findList(NewsAuthDto dto) {
        //1.校验参数
        dto.checkParam();
        if (StringUtils.isNotBlank(dto.getTitle())) {
            dto.setTitle("%"+dto.getTitle()+"%");
        }
        Integer page = dto.getPage();
        dto.setPage((page - 1) * dto.getSize());
        //2.调用mapper查询list   查询总数量
        List<WmNewsVo> listAndPage = wmNewsMapper.findListAndPage(dto);
        long count = wmNewsMapper.findListCount(dto);
        //3.封装返回结果
        PageResponseResult responseResult = new PageResponseResult(page, dto.getSize(), count, listAndPage);
        return responseResult;
    }

    @Override
    public ResponseResult findWmNewsVo(Integer id) {
        return ResponseResult.okResult(wmNewsMapper.findWmNewsVo(id));
    }

    /**
     * 人工审核方法
     * @param dto
     * @param status  2  审核失败  4 审核成功
     * @return
     */
    @Override
    public ResponseResult updateStatus(NewsAuthDto dto, short status) {
        //1.校验参数
        if (dto.getId() == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章id不能为空");
        }
        //2.根据id查询wmNews
        WmNews wmNews = getById(dto.getId());
        if (wmNews == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章信息不存在");
        }
        //3.修改状态和原因
        wmNews.setStatus(status);
        if (StringUtils.isNotBlank(dto.getMsg())) {
            wmNews.setReason(dto.getMsg());
        }
        updateById(wmNews);
        //4.返回结果
        return ResponseResult.okResult();
    }

    /**
     *
     * @param wmNews 文章信息
     * @param contentUrlList 内容图片列表
     * @param wmNewsDto 前台传入参数
     */
    private void saveRelativeInfoForCover(WmNews wmNews, List<String> contentUrlList, WmNewsDto wmNewsDto) {
        //1.获取图片封面
        List<String> images = wmNewsDto.getImages();
        //2.如果dto type为-1 代表自动生成封面
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(wmNewsDto.getType())) {
            if (contentUrlList == null || contentUrlList.size() == 0) {
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            } else if (contentUrlList.size() > 0 && contentUrlList.size() <= 2) {
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                images = contentUrlList.stream().limit(1).collect(Collectors.toList());
            } else {
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                images = contentUrlList.stream().limit(3).collect(Collectors.toList());
            }
            if (images != null && images.size() > 0) {
                wmNews.setImages(imageToStr(images));
                updateById(wmNews);
            }
        }
        //保存封面与素材的关联关系
        if (images != null && images.size() > 0) {
            images = images.stream().map(url -> url.replaceAll(webSite, "")).collect(Collectors.toList());
            saveRelativeInfo(wmNews.getId(), images, WemediaConstants.WM_IMAGE_REFERENCE);
        }
    }

    @Autowired
    WmMaterialMapper wmMaterialMapper;

    /**
     * 保存文章和素材的关联关系
     * 文章中引用的url 必须在素材中存在，不存在则保存失败
     * @param newsId 文章id
     * @param materialUrls 素材的url列表
     * @param refType 引用类型 ：0 内容引用 ， 1 封面引用
     */
    private void saveRelativeInfo(Integer newsId, List<String> materialUrls, Short refType) {
        //1.先根据url列表
        List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(
                Wrappers.<WmMaterial>lambdaQuery().eq(WmMaterial::getUserId,WmThreadLocalUtils.getUser().getId())
                .in(WmMaterial::getUrl,materialUrls)
        );
        if (wmMaterials == null || wmMaterials.size() == 0) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ERROR,"关联的指定素材不存在");
        }
        //2.提取出素材列表中的id和素材url路径，用map封装: key: 素材url value:id
        Map<String, Integer> urlAndIdMap = wmMaterials.stream()
                .collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));
        //3.判断传入的素材，是否全部存在，如果存在，收集每个素材对应的id
        List<Integer> materialIds = new ArrayList<>();
        materialUrls.forEach((url)->{
            if (urlAndIdMap.containsKey(url)) {
                CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
            }
            materialIds.add(urlAndIdMap.get(url));
        });
        //保存关联关系
        wmNewsMaterialMapper.saveRelations(materialIds, newsId, refType);


    }

    //抽取文章内容中的图片
    private List<String> parseContentImages(List<Map> contentMaps) {
        List<String> collectList = contentMaps.stream()
                .filter(map -> WemediaConstants.WM_NEWS_TYPE_IMAGE.equals(map.get("type")))
                .map(map -> (String) map.get("value"))
                .map(url -> url.replaceAll(webSite, ""))
                .collect(Collectors.toList());
        return collectList;
    }


     // 图片列表转字符串，并去除图片前缀
    private String imageToStr(List<String> imagesList) {
        return  imagesList.stream()
                .map(str -> str.replaceAll(webSite, ""))
                .collect(Collectors.joining(","));
    }

    @Autowired
    WmNewsMaterialMapper wmNewsMaterialMapper;

    //保存或修改文章
    private void saveOrUpdateWmNews(WmNews wmNews) {
        //1.补全wmNews信息，创建时间
        wmNews.setCreatedTime(new Date());
        wmNews.setSubmitedTime(new Date());
        //2.判断id是否为空
        if (wmNews.getId() == null) {
            //id为空 保存给wmNews
            save(wmNews);
        } else {
            //id不为空 先删除素材和文章的关系 再修改文章
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId()));
            updateById(wmNews);
        }

    }





}
